﻿namespace Nomo.UnityCoreModule.Waiters
{
    /// <summary>
    /// 等待器。
    /// </summary>
    public abstract class Waiter : System.Collections.IEnumerator
    {
        /// <summary>
        /// 获取一个受时间缩放影响的时间等待器。它会在内部维护一个计时器，并等待到计时器的值累加至给定的时长。
        /// </summary>
        /// <param name="waitTime">要等待的总时长。</param>
        /// <returns>受时间缩放影响的时间等待器。</returns>
        public static ScaledTimeWaiter ScaledTime(float waitTime)
        {
            return new ScaledTimeWaiter(waitTime);
        }

        /// <summary>
        /// 获取一个真实时间等待器。它会在内部维护一个计时器，并等待到计时器的值累加至给定的时长。
        /// </summary>
        /// <param name="waitTime">要等待的总时长。</param>
        /// <returns>真实时间等待器。</returns>
        public static RealTimeWaiter RealTime(float waitTime)
        {
            return new RealTimeWaiter(waitTime);
        }

        /// <summary>
        /// 获取一个进度等待器。它会等待到它的时间等待器等待完毕。
        /// </summary>
        /// <param name="timeWaiter">进度等待器的时间等待器。</param>
        /// <param name="onProgressUpdateAction">当进度更新时要执行的方法。</param>
        /// <returns>进度等待器。</returns>
        public static ProgressWaiter Progress(TimeWaiter timeWaiter, System.Action<float> onProgressUpdateAction)
        {
            return new ProgressWaiter(timeWaiter, onProgressUpdateAction);
        }

        /// <summary>
        /// 获取一个真等待器。它会等待到传入的谓词的返回值为 <see langword="true"/> .
        /// </summary>
        /// <param name="predicate">谓词。</param>
        /// <returns>真等待器。</returns>
        public static TrueWaiter True(System.Func<bool> predicate)
        {
            return new TrueWaiter(predicate);
        }

        /// <summary>
        /// 获取一个假等待器。它会等待到传入的谓词的返回值为 <see langword="false"/> .
        /// </summary>
        /// <param name="predicate">谓词。</param>
        /// <returns>假等待器。</returns>
        public static FalseWaiter False(System.Func<bool> predicate)
        {
            return new FalseWaiter(predicate);
        }

        /// <summary>
        /// 获取一个与等待器。它会等待到它的两个子等待器都等待完毕。
        /// </summary>
        /// <param name="waiter1">与等待器的第一个子等待器。</param>
        /// <param name="waiter2">与等待器的第二个子等待器。</param>
        /// <returns>与等待器。</returns>
        public static AndWaiter And(Waiter waiter1, Waiter waiter2)
        {
            return new AndWaiter(waiter1, waiter2);
        }

        /// <summary>
        /// 获取一个或等待器。它会等待到它的两个子等待器中的至少一个等待完毕。
        /// </summary>
        /// <param name="waiter1">或等待器的第一个子等待器。</param>
        /// <param name="waiter2">或等待器的第二个子等待器。</param>
        /// <returns>或等待器。</returns>
        public static OrWaiter Or(Waiter waiter1, Waiter waiter2)
        {
            return new OrWaiter(waiter1, waiter2);
        }

        /// <summary>
        /// 获取一个与等待器。它会等待到它的两个子等待器都等待完毕。
        /// </summary>
        /// <param name="waiter1">与等待器的第一个子等待器。</param>
        /// <param name="waiter2">与等待器的第二个子等待器。</param>
        /// <returns>与等待器。</returns>
        public static AndWaiter operator &(Waiter waiter1, Waiter waiter2)
        {
            return new AndWaiter(waiter1, waiter2);
        }

        /// <summary>
        /// 获取一个或等待器。它会等待到它的两个子等待器中的至少一个等待完毕。
        /// </summary>
        /// <param name="waiter1">或等待器的第一个子等待器。</param>
        /// <param name="waiter2">或等待器的第二个子等待器。</param>
        /// <returns>或等待器。</returns>
        public static OrWaiter operator |(Waiter waiter1, Waiter waiter2)
        {
            return new OrWaiter(waiter1, waiter2);
        }

        private int _frameCount;

        /// <summary>
        /// 是否等待完毕？
        /// </summary>
        public bool Completed { get; private set; }

        private protected abstract bool KeepWaiting { get; }

        private protected Waiter()
        {
            _frameCount = UnityEngine.Time.frameCount;
        }

        /// <inheritdoc />
        public virtual bool MoveNext()
        {
            var frameCount = UnityEngine.Time.frameCount;
            if (_frameCount == frameCount)
            {
                return !Completed;
            }
            _frameCount = frameCount;
            var keepWaiting = KeepWaiting;
            Completed = !keepWaiting;
            return keepWaiting;
        }

        /// <inheritdoc />
        public object Current => null;

        /// <summary>
        /// 重置此等待器，并重置此等待器的任何子等待器（如果有）。
        /// </summary>
        public virtual void Reset()
        {
            _frameCount = UnityEngine.Time.frameCount;
            Completed   = false;
        }
    }
}
